/********************************************************************************
 * Copyright(c) 2020-2025 VINCENT_WY All rights reserved.
 * - Filename
 * - Author  Vincent
 * - Version V1.0.0
 * - Date    2023/09/21
 * - Brief
 * - FunctionList:
 ******************************************************************************
 * History:
 *
 *
 *
 ******************************************************************************
 */

#include "DispatchReplayer.h"
#include "TimeUtil.h"
#include "logutil.h"
#include "nlohmann/json.hpp"
#include <fstream>

#define MAX_BUFFER 200

using njson = nlohmann::json;

DispatchReplayer::DispatchReplayer(const std::string &cfg) : _cfgFile(cfg) {}
DispatchReplayer::~DispatchReplayer() {}
void DispatchReplayer::start(
    const std::function<void(std::shared_ptr<CameraFrame> &&)> &callback,
    const std::function<void(std::shared_ptr<BevFrame> &&)> &bc) {
  creatReplayImpl();
  _mThreadPool = std::make_shared<ThreadPool>(1);
  _mThreadPool->init();
  for (auto &&impl : _impl) {
    impl->start(callback, bc, _allFrameStartTime);
  }
  if (_repeat) {
    _loopThread = std::make_unique<std::thread>(&DispatchReplayer::loop, this);
  }
  LOGD("Replay is running ...");
}
void DispatchReplayer::stop() {
  _isExit = true;
  _mThreadPool->shutDown();
  if (_loopThread != nullptr && _loopThread->joinable()) {
    _loopThread->join();
  };
  LOGD("DispatchReplayer Stop ......");
  for (auto &&impl : _impl) {
    impl->stop();
  }
  LOGD("[Replay]  exit ...");
}

void DispatchReplayer::creatReplayImpl() {
  std::fstream cfgStream{_cfgFile};
  std::vector<long long int> frameStartTime;
  njson cfg;
  try {
    cfgStream >> cfg;
    _repeat = cfg["player"]["repeat"].get<bool>();
    _rootPath = cfg["player"]["path"].get<std::string>();
    auto source = cfg.at("source");
    // camera
    for (auto &camera : source) {
      auto id = camera.at("camera_id").get<int>();
      auto name = camera.at("camera_name").get<std::string>();
      auto player =
          std::make_shared<DispatchReplayImpl>(id, _rootPath + "/" + name);

      dispatch::FileMap mFileMap;
      mFileMap = player->getFile();
      _allFileMap.insert(mFileMap.begin(), mFileMap.end());
      _impl.push_back(player);
    }

    // can
    // {
    // }
    _allFrameStartTime = (*_allFileMap.begin()).first;
  } catch (const std::exception &e) {
    LOGE("get config from %s error: %s", _cfgFile.c_str(), e.what());
    return;
  }
}
void DispatchReplayer::loop() {
  int mRepeatCount{0};
  bool mReadFile{true};
  while (!_isExit) {
    if (mReadFile) {
      mReadFile = false;
      auto func =
          std::bind(&DispatchReplayer::FileRead, this, std::placeholders::_1);
      _mThreadPool->submit(func, _allFileMap);
    }

    mRepeatCount = 0;
    for (auto &&impl : _impl) {
      if (impl->isComplete()) {
        mRepeatCount++;
      }
    }
    // 开启重复，每一路都回灌完成，则重新回灌
    if (_repeat && (mRepeatCount == (int)_impl.size())) {
      mReadFile = true;
      LOGD("Do Repeat ......");
      for (auto &&impl : _impl) {
        impl->doRepeat();
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(5000));
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
}

void DispatchReplayer::FileRead(dispatch::FileMap &map) {
  int mReadCount = 0;
  for (dispatch::FileMap::iterator it = map.begin(); it != map.end(); it++) {

    if (_isExit) {
      break;
    }
    if (mReadCount++ == MAX_BUFFER) {
      for (auto &&impl : _impl) {
        impl->doReplay();
      }
    }
    std::shared_ptr<uint8_t[]> nv12_buffer(
        new uint8_t[(*it).second.width * (*it).second.height * 3 / 2],
        [](uint8_t *p) { delete[] p; });
    // auto t1 = TimeUtil::now_us();
    FILE *fp_r = fopen((*it).second.fileName.c_str(), "rb");
    if (NULL == fp_r) {
      LOGE("failed to open the fp_r");
      fclose(fp_r);
      return;
    }
    // auto t2 = TimeUtil::now_us();
    // 将文件数据读到image buffer中
    fread(nv12_buffer.get(), sizeof(unsigned char),
          (*it).second.width * (*it).second.height * 3 / 2, fp_r);
    // auto t3 = TimeUtil::now_us();
    std::this_thread::sleep_for(std::chrono::milliseconds(2));
    fclose(fp_r);

    // auto t4 = TimeUtil::now_us();
    // printf("id:%d , freq: %d, open time: %lld us, read time: %lld us,  "
    //        "total:%lld us \n",
    //        (*it).second.type, (*it).second.freq, t2 - t1, t3 - t2, t3 - t1);
    std::shared_ptr<CameraFrame> frame = std::make_shared<CameraFrame>();
    frame->cameraId = (*it).second.type;
    frame->width = (*it).second.width;
    frame->height = (*it).second.height;
    frame->timestamp.tv_sec = (*it).second.timestamp / 1000000;
    frame->timestamp.tv_usec = (*it).second.timestamp % 1000000;
    frame->buffer = std::move(nv12_buffer);

    if (_isExit) {
      break;
    }
    _impl[(int)(*it).second.type]->submit(frame);
  }
}